package com.cgw.auth.Interceptor;

import com.cgw.auth.attribute.AuthApiAttribute;
import com.cgw.auth.constance.AuthConst;
import com.cgw.auth.entity.AuthorizeInfo;
import com.cgw.auth.enumer.AuthRedisKey;
import com.cgw.auth.enumer.AuthStatus;
import com.cgw.auth.expection.AuthException;
import com.cgw.auth.service.AuthorizeInfoService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.gavin.core.domain.R;
import org.gavin.core.enums.CommonStatus;
import org.gavin.core.utils.GMSM4Utils;
import org.gavin.core.utils.JsonUtils;
import org.redis.service.RedisService;
import org.springframework.util.DigestUtils;
import org.springframework.web.servlet.HandlerInterceptor;

import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;

/**
 * AuthInterceptor
 *
 * @author grl
 * @date 2024/4/28
 */
@Slf4j
public class AuthInterceptor implements HandlerInterceptor {

    private final AuthApiAttribute authApiAttribute;
    private final RedisService redisService;
    private final AuthorizeInfoService authorizeInfoService;

    public AuthInterceptor(AuthApiAttribute authApiAttribute, RedisService redisService, AuthorizeInfoService authorizeInfoService) {
        this.authApiAttribute = authApiAttribute;
        this.authorizeInfoService = authorizeInfoService;
        this.redisService = redisService;
    }

    /**
     * 目标方法执行之前
     * 登录检查写在这里，如果没有登录，就不执行目标方法
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        AuthorizeInfo auth = getAndCheckAppId(request, response);
        // 校验是请求否过期
        checkSignature(request, auth);
        return true;
    }

    private AuthorizeInfo getAndCheckAppId(HttpServletRequest request, HttpServletResponse response) {
        String appId = request.getHeader(AuthConst.Header_APPID);
        AuthorizeInfo authorizeInfo = getAuthInfo(appId);
        if (authorizeInfo == null) {
            throw AuthException.le(AuthStatus.APPID_VALUE_INVALID, AuthConst.MODEL_NAME, appId);
        }
        return authorizeInfo;
    }

    private AuthorizeInfo getAuthInfo(String appId) {
        AuthRedisKey detailsKey = AuthRedisKey.AUTH_DETAILS;
        detailsKey.setKey(appId);
        AuthorizeInfo authorizeInfo = redisService.get(detailsKey, AuthorizeInfo.class);
        if (authorizeInfo == null) {
            authorizeInfo = authorizeInfoService.getAuth(appId);
        }
        return authorizeInfo;
    }

    private void checkSignature(HttpServletRequest request, AuthorizeInfo auth) {
        String encSignature = request.getHeader(AuthConst.Header_SIGNATURE);
        if (StringUtils.isBlank(encSignature)) {
            throw AuthException.le(AuthStatus.SIGNATURE_VALUE_INVALID, AuthConst.MODEL_NAME, encSignature);
        }
        // 解密签名
        String signature = decSignature(auth, encSignature);
        if (StringUtils.isBlank(signature) || signature.length() < AuthConst.SIGNATURE_LENGTH) {
            throw AuthException.le(AuthStatus.SIGNATURE_VALUE_INVALID, AuthConst.MODEL_NAME, signature);
        }
        long times;
        try {
            times = Long.parseLong(signature.substring(32));
        } catch (NumberFormatException e) {
            log.error("签名戳转时间times错误! 签名戳 {}", signature, e);
            throw AuthException.le(AuthStatus.INVALID_TIMESTAMP, AuthConst.MODEL_NAME, signature);
        }
        long interval = Math.abs((System.currentTimeMillis() - times) / (1000 * 60));
        if (authApiAttribute.getExpire() < interval) {
            throw AuthException.le(AuthStatus.OVER_TIME_SIGNATURE, AuthConst.MODEL_NAME, authApiAttribute.getExpire());
        }
    }

    public String decSignature(AuthorizeInfo auth, String encSignature) {
        String secretKey = auth.getSecretKey();
        String encSecretKey = DigestUtils.md5DigestAsHex(secretKey.getBytes(StandardCharsets.UTF_8));
        return GMSM4Utils.decryptEcb(encSignature, encSecretKey);
    }

}
